package videoGrabber;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.media.Buffer;
import javax.media.CannotRealizeException;
import javax.media.Codec;
import javax.media.Format;
import javax.media.Manager;
import javax.media.NoPlayerException;
import javax.media.NoProcessorException;
import javax.media.NotConfiguredError;
import javax.media.Player;
import javax.media.Processor;
import javax.media.UnsupportedPlugInException;
import javax.media.control.FrameGrabbingControl;
import javax.media.control.TrackControl;
import javax.media.format.VideoFormat;
import javax.media.util.BufferToImage;

import view.Gui;
import codecs.BorderCodec;
import codecs.BorderCodecControlInterface;
import codecs.DifferenceCodec;
import codecs.DifferenceCodecControlInterface;

/**
 * Manages the supply of the Datastream for manpulation
 * @author Daniel Mohr, Daniela Grammlich
 */
public class VideoGrabber {
	private int imageNumber = 0;
	private Player player = null;
	private Processor processor = null;
	private javax.media.protocol.DataSource dataSource = null;
	private Codec[] codecs;

	public FrameGrabbingControl grabbingControl;
	public Buffer buffer;
	public Image image;
	public BufferToImage bufToIm;
	public BufferedImage bufferedIm;

	Gui myGui;	
	CamAccess access;

	/**
	 * Constructor of class
	 */
	public VideoGrabber(CamAccess access2, boolean withCodecs){
		this.access = access2;		
		this.processor = initProcessor(withCodecs);
		this.player = initPlayer();
	}

	/**
	 * Initializes the processor on the given CaptureDevice
	 * @throws NoProcessorException, IOException
	 */
	public Processor initProcessor(boolean withCodecs)
	{	
		try {			
			this.processor = Manager.createProcessor(this.access.getMediaLocator());
			this.processor.configure();
			while(processor.getState() != processor.Configured){
				Thread.sleep(50);
			}
			
			TrackControl[] tracks = this.processor.getTrackControls();
			if(withCodecs) {
				initCodecs(tracks);
			}

			Thread.sleep(500);

			codecs[2] = new DifferenceCodec((BorderCodecControlInterface)codecs[1].getControl("null"));
//			codecs[3] = new TheGridEffect();
//			codecs[3] = new BinaryCodec((DifferenceCodecControlInterface)codecs[2].getControl("null"));
			
			tracks[0].setCodecChain(codecs);
			this.processor.realize();
			while(processor.getState() != processor.Realized){
				Thread.sleep(50);
			}	
			this.processor.setContentDescriptor(null);
			this.dataSource = processor.getDataOutput();
			this.processor.start();
		} catch (UnsupportedPlugInException e) {
			e.printStackTrace();
		} catch (NotConfiguredError e) {
			e.printStackTrace();
		} catch (NoProcessorException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (IOException e) {				
			e.printStackTrace();
		}
		return processor;
	}

	/**
	 * Initializes the video player on the given processor to display the manipulation of the stream
	 * @throws NoPLayerException, IOException, CannotRealizeException
	 */
	public Player initPlayer(){
		try {
			Manager.setHint(Manager.LIGHTWEIGHT_RENDERER, new Boolean(true));
			player = Manager.createRealizedPlayer(this.dataSource);		
		} catch (NoPlayerException e) {
			System.out.println("Error: NoPlayerException");
			e.printStackTrace();
		} catch (IOException e) {
			System.out.println("Error: IOException");
			e.printStackTrace();
		} catch (CannotRealizeException e) {
			e.printStackTrace();
		}
		return player;
	}

	/**
	 * Initializes the codecs
	 */
	public void initCodecs(TrackControl[] tracks)
	{
		for (int i = 0; i < tracks.length; i++) {
			Format format = tracks[i].getFormat();
			System.out.println(format);
			try
			{
				/* List of codecs */
				codecs = new Codec[]{
					new com.sun.media.codec.video.cinepak.JavaDecoder(),
					new BorderCodec(),				
					null, // DifferenceCodec: is set later
					null, // BinaryCodec: test case
				};
				tracks[i].setCodecChain(codecs);
				System.out.println(i + ") " + codecs[i].getSupportedInputFormats());
			} catch(UnsupportedPlugInException ex) {
				ex.printStackTrace();
			}	
		}
	}

	/**
	 * Continue streaming
	 */
	public void startPlayer()
	{
		if(player != null){
			player.start();
			player.realize();
		}
	}

	/**
	 * Stops the visualization of the stream
	 */
	public void stopPlayer()
	{
		player.stop();
	}

	/**
	 * Stops and closes processing on the datasource
	 */
	public void ProcessorClose()
	{
		this.processor.close();
		this.processor.deallocate();
	}

	/**
	 * Stops and closes streaming 
	 */
	public void playerClose()
	{
		player.close();
		player.deallocate();
	}

	/**
	 * getter for <code>{@link #player}</code>
	 */
	public Player getPlayer()
	{
		return this.player;
	}

	/**
	 * getter for <code>{@link #processor}</code>
	 */
	public Processor getProcessor()
	{
		return this.processor;
	}

	/**
	 * Grabs the current frame from the stream
	 */
	public void grabFrame() throws IOException{
		grabbingControl = (FrameGrabbingControl)this.player.getControl("" + "javax.media.control.FrameGrabbingControl");
		buffer = grabbingControl.grabFrame();
		bufToIm = new BufferToImage((VideoFormat)buffer.getFormat());
		image = bufToIm.createImage(buffer);    
	}

	/**
	 * Saves the grabbed frame to an png file
	 * @throws IOException, FileNotFoundException
	 */
	public void saveFrame() {
		bufferedIm = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB_PRE);
		Graphics2D g2 = bufferedIm.createGraphics();
		g2.drawImage(image, null, null);
		FileOutputStream out;
		try {
			out = new FileOutputStream("Image" + imageNumber +  ".png");
			ImageIO.write((RenderedImage) image, "png", out);
			this.imageNumber++;
			try {
				out.close();
			}
			catch (java.io.IOException io) {
				System.out.println("IOException");
				System.err.println(io.getMessage());
			}
		}
		catch (java.io.FileNotFoundException fnf) {
			System.out.println("File Not Found");
			System.err.println(fnf.getMessage());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Closes the xml handling
	 */
	public void closeXML() {
		DifferenceCodecControlInterface dcci;
		dcci = (DifferenceCodecControlInterface) codecs[2].getControl("null");
		if(dcci != null){			
			dcci.getXmlGenerator().writeXML();
		}
	}
	
}
